<?php
/**
 * 验证码图片生成器
 * 
 * CAPTCHA 是“completely automated public Turing test to tell computers and humans apart”的缩写 *，已由卡内基梅隆大学注册商标。 
 * 是一种区分用户是计算机和人的计算程序。
 *
 * @package if
 * @subpackage module.image
 * @since 1.0
 */
class ICaptcha extends IObject {
	private $font;
	private $width = 46;
	private $height = 20;
	private $text;
	private $chars = "23456789abcdfghijkmnpqrstvwxyzABCDFGHIJKMNPQRSTVWXYZ";
	private static $defaultId = "TEXT";
	private $id = "TEXT";
	private $length = 4;
	private $lines = 4;
	private $dots = 30;
	private $backgroundColor = "#FFFFFF";
	private $textColor = "#142864";
	private $noiseColor = "#999999";
	private $borderColor = "#999999";
	private $backgroundImage;
	
	/**
	 * 构造器
	 *
	 * 支持的参数：
	 * - id - 验证码的ID, 
	 * - font - 字体文件
	 * - width - 画布宽度
	 * - height - 画布高度
	 * - text - 要绘制的文本 
	 * - length - 如果没指定要绘制的文本，则依此长度生成一段随机的文本
	 * - lines - 干扰线的数目
	 * - dots - 干扰点的数目
	 * - backgroundColor - 背景色
	 * - backgroundImage - 背景图，和backgroundColor只能二选一
	 * - textColor - 文本颜色
	 * - noiseColor - 干扰点和线的颜色
	 * - borderColor - 边框颜色
	 * 所有参数都是可选的
	 * 
	 * @param array|null $options 选项
	 * @since 1.0
	 */
	function __construct(array $options = null) {
		if (!function_exists("imagecreate")) {
			throw new IException("gd must be loaded before using captcha", 0x100016);
		}
		if (is_array($options)) {
			foreach (array(
				"font", 
				"width", 
				"height", 
				"text", 
				"id", 
				"length",
				"lines",
				"dots",
				"backgroundColor",
				"textColor",
				"noiseColor",
				"borderColor",
				"backgroundImage"
			) as $opt) {
				if (isset($options[$opt])) {
					$this->$opt = $options[$opt];
				}
			}
		}
		if ($this->font == "") {
//			$this->font = dirname(__FILE__) . "/fonts/cour.ttf";
		}
	}
	
	/**
	 * 绘制验证码
	 *
	 * @since 1.0
	 */
	function draw() {
		if ($this->text == "") {
			$this->text = $this->genText();
		}
		
      	$fontSize = $this->height * 0.75;
      	$image = null;
      	$outputFunc = "imagejpeg";
      	$contentType = "image/jpeg";
      	
      	//背景图
      	if (function_exists("imagecreatetruecolor")) {
      		$image = imagecreatetruecolor($this->width, $this->height);
      	}
      	else  {
      		$image = imagecreate($this->width, $this->height);
      	}
      	if ($this->backgroundImage) {
      		$file = new IFile($this->backgroundImage);
      		if (!$file->exists()) {
      			throw new IException("background image '{$this->backgroundImage}' does not exist", 0x100017);
      		}
      		$ext = $file->ext(true);
      		if (!in_array($ext, array("gif", "png", "jpg", "jpeg"))) {
      			throw new IException("file extension of background image must be 'gif','png',jpg' or 'jpeg'", 0x100018);
      		}
      		
      		$bgImage = null;
      		switch ($ext) {
      			case "gif":
      				$bgImage = imagecreatefromgif($file->path());
      				$outputFunc = "imagegif";
      				$contentType = "image/gif";
      				break;
      			case "png":
      				$bgImage = imagecreatefrompng($file->path());
      				$outputFunc = "imagepng";
      				$contentType = "image/png";
      				break;
      			case "jpg":
      			case "jpeg":
      				$bgImage = imagecreatefromjpeg($file->path());
      				$outputFunc = "imagejpeg";
      				$contentType = "image/jpeg";
      				break;
      		}
      		
      		//判断
	      	if (!$bgImage) {
	      		throw new IException("failed to create image, may be caused by bad file format", 0x100019);
	      	}
	      	
	      	imagecopyresampled($image, $bgImage, 0, 0, 0, 0, $this->width, $this->height, $this->width, $this->height);
	      	imagedestroy($bgImage);
      	}
      	
      	
      
      	//颜色
      	$backgroundColor = null;
      	if (!$this->backgroundImage) {
	      	$rgb1 = $this->rgb($this->backgroundColor);
	      	$backgroundColor = imagecolorallocate($image, $rgb1[0], $rgb1[1], $rgb1[2]);
      	}
      	$rgb1 = $this->rgb($this->textColor);
      	$textColor = imagecolorallocate($image, $rgb1[0], $rgb1[1], $rgb1[2]);
      	$rgb1 = $this->rgb($this->noiseColor);
      	$noiseColor = imagecolorallocate($image, $rgb1[0], $rgb1[1], $rgb1[2]);
      	
      	//背景
      	if (!$this->backgroundImage) {
      		imagefilledrectangle($image, 0, 0, $this->width - 1, $this->height - 1, $backgroundColor);
      	}
      	
      	//边框
      	if ($this->borderColor) {
      		$rgb1 = $this->rgb($this->borderColor);
      		$borderColor = imagecolorallocate($image, $rgb1[0], $rgb1[1], $rgb1[2]);
      		imagerectangle($image, 0, 0, $this->width - 1, $this->height - 1, $borderColor);
      	}
        
      	//生成干扰点
      	for( $i=0; $i< $this->dots; $i++ ) {
         	imagefilledellipse($image, mt_rand(0, $this->width), mt_rand(0, $this->height), 1, 1, $noiseColor);
      	}
     
      	//生成干扰线
      	for( $i=0; $i< $this->lines; $i++ ) {
         	imageline($image, mt_rand(0, $this->width), mt_rand(0, $this->height), mt_rand(0, $this->width), mt_rand(0, $this->height), $noiseColor);
      	}
     
      	//生成
      	$draw = false;
      	if ($this->font != "") {
	      	$textbox = imagettfbbox($fontSize, 0, $this->font, $this->text);
	      	if (!$textbox) {
	      		throw new IException("error occurred when load ttf (imagettfbbox)", 0x100020);
	      	}
	      	$x = ($this->width - $textbox[4])/2;
	      	$y = ($this->height - $textbox[5])/2;
	      	$draw = imagettftext($image, $fontSize, 0, $x, $y, $textColor, $this->font , $this->text);
      	}
      	else {
      		$draw = imagestring($image, $fontSize, 5, 3, $this->text, $textColor);
      	}
      	if (!$draw) {
      		throw new IException("error occurred when draw text (imagettftext)", 0x100021);
      	}
      	    
      	//输出到浏览器
		x("#IF_CAPTCHA_{$this->id}", $this->text);
      	header("Content-Type: " . $contentType);
      	$outputFunc($image);
      	imagedestroy($image);
	}
	
	/**
	 * 生成随机的文本
	 *
	 * @return string
	 * @since 1.0
	 */
	private function genText() {
		$max = strlen($this->chars);
		$text = "";
		for ($i=0; $i<$this->length; $i++) {
			$rand = mt_rand(0, $max - 1);
			$text .= $this->chars{$rand};
		}
		return $text;
	}
	
	private function rgb($color) {
		$rgb = array();
		if ($color == "") {
			$rgb = array(255, 255, 255);
		}
		else if ($color{0} == "#") {
			$rgb = IColor::hex2rgb($color);
		}
		else {
			$rgb = IColor::rgb($color);
		}
		if (empty($rgb)) {
			throw new IException("unknown color '{$color}'", 0x100022);
		}
		return $rgb;
	}
	
	/**
	 * 取得验证码中的文本
	 *
	 * @param string|null $id 验证码的ID
	 * @return string
	 * @since 1.0
	 */
	static function text($id = null) {
		if ($id == "") {
			$id = self::$defaultId;
		}
		return x("#IF_CAPTCHA_{$id}");
	}
	
	/**
	 * 清除SESSION中验证码信息
	 *
	 * @param string|null $id 验证码的ID
	 * @since 1.0
	 */
	static function clear($id = null) {
		x("#IF_CAPTCHA_{$id}", null);
	}
	
	/**
	 * 判断验证码是否和接受的参数的值一致
	 *
	 * @param string $param 参数名
	 * @param boolean $nocase 是否不区分大小写，默认为是
	 * @param string|null $id 验证码的ID
	 * @return boolean
	 * @since 1.0
	 */
	static function equal($param, $nocase = true, $id = null) {
		$value1 = trim(x($param));
		$value2 = self::text($id);
		if ($nocase) {
			return strtolower($value1) == strtolower($value2);
		}
		return $value1 == $value2;
	}
	
	/**
	 * 设置随机用的字符
	 * 
	 * 例子
	 * - 只显示数字：$captcha->setChars(range(0, 9));
	 * - 只显示字母：$captcha->setChars(range(a, z));
	 *
	 * @param string|array $chars 一组字符
	 * @since 1.0
	 */
	function setChars($chars) {
		$this->chars = is_array($chars)?implode("", $chars) : $chars;
	}
}

?>